package cn.dev33.satoken.filter;

import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.oauth2.logic.type.SecurityStrategy;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.util.SaTokenConsts;
import com.csap.framework.util.map.CsapMap;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Servlet全局过滤器
 *
 * @author kong
 */
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaServletFilter implements Filter {
    /**
     * 拦截路由
     */
    private Map<HttpMethod, Map<SecurityStrategy, Set<String>>> filter = new HashMap<>(16);

    /**
     * 查询是否有响应的策略
     *
     * @param httpMethod       http方法
     * @param securityStrategy 权限策略
     * @return 是否存在
     */
    public Boolean containsSecurityStrategy(HttpMethod httpMethod, SecurityStrategy securityStrategy) {
        return filter.containsKey(httpMethod) && filter.get(httpMethod).containsKey(securityStrategy);
    }

    /**
     * 添加 [拦截路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addInclude(String... paths) {
        return addInclude(HttpMethod.values(), Arrays.asList(paths));
    }

    /**
     * 添加 [拦截路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addInclude(HttpMethod[] httpMethod, Collection<String> paths) {
        for (HttpMethod method : httpMethod) {
            addAll(method, SecurityStrategy.authenticated, paths);
        }
        return this;
    }

    /**
     * 添加 [拦截路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addInclude(HttpMethod httpMethod, Collection<String> paths) {
        return addAll(httpMethod, SecurityStrategy.authenticated, paths);
    }

    /**
     * 添加拦截路径
     *
     * @param httpMethod
     * @param filterType
     * @param paths
     * @return
     */
    public SaServletFilter addAll(HttpMethod httpMethod, SecurityStrategy filterType, Collection<String> paths) {
        filter.computeIfAbsent(httpMethod, v -> new CsapMap<>()).computeIfAbsent(filterType, v -> new HashSet<>()).addAll(paths);
        return this;
    }

    /**
     * 添加 [放行路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addExclude(String... paths) {
        return addExclude(Arrays.asList(paths));
    }

    /**
     * 添加 [放行路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addExclude(HttpMethod httpMethod, Collection<String> paths) {
        return addAll(httpMethod, SecurityStrategy.permitAll, paths);
    }

    /**
     * 添加放行路径
     *
     * @param httpMethod 方法列表
     * @param paths      添加的路径
     * @return 自身
     */
    public SaServletFilter addExclude(HttpMethod[] httpMethod, Collection<String> paths) {
        for (HttpMethod method : httpMethod) {
            addAll(method, SecurityStrategy.permitAll, paths);
        }
        return this;
    }

    /**
     * 添加 [放行路由]
     *
     * @param paths 路由
     * @return 对象自身
     */
    public SaServletFilter addExclude(Collection<String> paths) {
        return addAll(HttpMethod.GET, SecurityStrategy.permitAll, paths);
    }

    /**
     * 认证函数：每次请求执行
     */
    public SaFilterAuthStrategy auth = r -> {
    };

    /**
     * 异常处理函数：每次[认证函数]发生异常时执行此函数
     */
    public SaFilterErrorStrategy error = e -> {
        throw new SaTokenException(e);
    };

    /**
     * 前置函数：在每次[认证函数]之前执行
     */
    public SaFilterAuthStrategy beforeAuth = r -> {
    };

    /**
     * 写入[认证函数]: 每次请求执行
     *
     * @param auth see note
     * @return 对象自身
     */
    public SaServletFilter setAuth(SaFilterAuthStrategy auth) {
        this.auth = auth;
        return this;
    }

    /**
     * 写入[异常处理函数]：每次[认证函数]发生异常时执行此函数
     *
     * @param error see note
     * @return 对象自身
     */
    public SaServletFilter setError(SaFilterErrorStrategy error) {
        this.error = error;
        return this;
    }

    /**
     * 写入[前置函数]：在每次[认证函数]之前执行
     *
     * @param beforeAuth see note
     * @return 对象自身
     */
    public SaServletFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
        this.beforeAuth = beforeAuth;
        return this;
    }


    // ------------------------ doFilter

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            // 执行全局过滤器
            filterMapping((HttpServletRequest) request);
        } catch (StopMatchException e) {

        } catch (Throwable e) {
            // 1. 获取异常处理策略结果
            String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e));
            // 2. 写入输出流
            if (response.getContentType() == null) {
                response.setContentType("text/plain; charset=utf-8");
            }
            response.getWriter().print(result);
            return;
        }
        // 执行
        chain.doFilter(request, response);
    }

    /**
     * 获取权限列表
     *
     * @param method           当前方法
     * @param securityStrategy 当前验证的权限策略
     * @return 权限列表
     */
    private Set<String> getSecuritys(HttpMethod method, SecurityStrategy securityStrategy) {
        return filter.get(method).getOrDefault(securityStrategy, Collections.emptySet());
    }

    /**
     * 路径拦截处理
     *
     * @param request
     */
    private void filterMapping(HttpServletRequest request) {
        HttpMethod method = HttpMethod.resolve(request.getMethod());
        if (!filter.containsKey(method)) {
            return;
        }
        SaRouter.match(getSecuritys(method, SecurityStrategy.authenticated), getSecuritys(method, SecurityStrategy.permitAll), () -> {
            beforeAuth.run(null);
            auth.run(null);
        });
    }

    /**
     * 验证角色
     *
     * @param method 当前请求的方法类型
     */
    private void role(HttpMethod method) {

    }

    /**
     * 验证权限
     * @param method
     */
    private void permission(HttpMethod method) {

    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }


}
